﻿#include"head.h"
                        
Dlistp head;
typedef struct info{
	int fd;
	struct in_addr sin_addr;
	u_short sin_port;
	char buf[SIZE];
}Clientinfo,*Cp;
                                 
void add_intolist(int connectfd,struct sockaddr_in* info)
{
	Dlistp new = calloc(1,sizeof(struct list_head));
	if(new == NULL){
		perror("new node failed");
	}
	new->fd = connectfd;
	new->sin_addr.s_addr = info->sin_addr.s_addr;
	new->sin_port = info->sin_port;
	list_add_tail(new,head);
		
}

void* client_routine(void* arg)
{
	Dlistp tempnode = (Dlistp)arg;
	int newfd = tempnode->fd,num;
	Clientinfo ctos,stoc;
 	//将成功连接的client端套接字加入双向循环链表当中,方便后续管理这些client
	int readsize,writesize;
	Dlistp findnode;
	char buf[SIZE]={0};
	char buftxt[SIZE]={0};

	while(1)
	{	
		bzero(buf,SIZE);
		printf(" before free node  :\n");
		ergodic_list(head);
		//接收客户端发来的命令，并执行相应命令
		readsize = read(newfd,buf,SIZE);
		if(!strcmp(buf,"list"))
		{
			//计算节点个数
			num = count_numlist(head); 
			write(newfd,&num,4);
			printf("num:%d \n",num);
			Dlistp temp = head;
			
			//向客户端返回好友列表
			for(int i=0;i<num;i++)
			{
				temp =temp->next;
				write(newfd,temp,sizeof(struct list_head));
				printf("[昵称：] IP:%s  PORT:%hu\n",inet_ntoa(temp->sin_addr),ntohs(temp->sin_port));
			}
		
		}else if(!strcmp(buf,"chat"))
		{

			//读取来电消息，new->fd套接字中携带有接听电话客户端的一切信息
			readsize = read(newfd,&ctos,sizeof(Clientinfo));	
			if(readsize == -1)
			{
				perror("read failed");		
			}
					
			//遍历链表，查找需要接听的客户端
			findnode = fine_node(&ctos.sin_addr,head);
			if(findnode == head)
			{
				perror("node not exist!");
			}

			while(1)
			{	
				bzero(buftxt,SIZE);
				read(newfd,buftxt,SIZE);
				if(!strcmp(buftxt,"chat0001"))
				{
					readsize = read(newfd,buftxt,SIZE);
					strncpy(ctos.buf,buftxt,readsize);
					 
					writesize = write(findnode->fd,ctos.buf,strlen(ctos.buf));
					if(readsize == -1) 	
					{
						perror("write failed");
					}
					printf("---------sendto  ip:%s :%s\n",inet_ntoa(findnode->sin_addr),ctos.buf);
				}else if(!strcmp(buftxt,"sentfile"))
				{
					bzero(buftxt,SIZE);
					while((readsize = read(newfd,buftxt,SIZE)))
					{
						if(readsize > 0){
							write(newfd,buftxt,readsize);
						}else if(readsize <0)
						{
							perror("receive file error!");
						}	
					}
				}
			}		
		}else if(!strcmp(buf,"quit"))
		{	
				Dlistp findel = fine_node(&tempnode->sin_addr,head);
				printf("ip %s findel: %s\n",inet_ntoa(ctos.sin_addr),inet_ntoa(findel->sin_addr));
				if(findel!= head) //如果返回不是头节点，则将找到的释放
				{
					list_del(findel);
					close(newfd);
				}
				printf(" after free node  :\n");
				ergodic_list(head);
				break;
		}
	}
	pthread_exit(NULL);
}

int main(int argc,char* argv[])
{
	if(argc != 2)
	{
		fprintf(stderr,"usage: %s <PORT>\n",argv[0]);
		exit(1);
	}
	
	//创建套接字
	int socket_tcp = socket(AF_INET,SOCK_STREAM,0);
	if(socket_tcp == -1)
	{
		perror("socket failed");
		
	}

	//定义包含IP/PORT的结构体
	struct sockaddr_in serveraddr_info,client_info;
	serveraddr_info.sin_family = AF_INET;
	serveraddr_info.sin_port = htons(atoi(argv[1]));  //必须转换成网络字节序
	serveraddr_info.sin_addr.s_addr =  INADDR_ANY;    //指定为本地任意地址
	socklen_t serveraddr_len = sizeof(serveraddr_info);
	socklen_t client_len = sizeof(client_info);	

	//bind绑定套接字和网络地址
	int retbind = bind(socket_tcp,(struct sockaddr*)&serveraddr_info,serveraddr_len);
	if(retbind == -1)
	{
		perror("server bind failed");
	}
	
	//设置server可以同时端监听客户端的连接数量，同一时间，客户端可以连接server的数量
	int retlsn = listen(socket_tcp,BACKLOG );
	if(retlsn == -1){
		perror("listen failed");	
	}
	
	//初始化双向循环链表
	head = list_init();
	
	Dlist infot;
	int fd;
	while(1)
	{
 		//将成功连接的client端套接字加入双向循环链表当中,方便后续管理这些client
		//等待对方链接，并保存对端的IP、PORT
		
		infot.fd = accept(socket_tcp,(struct sockaddr*)&client_info,&client_len);
		if(infot.fd == -1)
		{
			perror("accept failed");
		}
	
		//添加新的节点前判断节点是否重复,则中断下面的执行
		Dlistp newnode =fine_node(&client_info.sin_addr,head);
		if(newnode !=head){
			printf("由于连接重复，此项连接无效！\n");
			continue;
		}
		
		//将接收到的对端信息保存到新节点当中
		add_intolist(infot.fd,&client_info);
		printf("ip-%s port-%hu \n",inet_ntoa(client_info.sin_addr),ntohs(client_info.sin_port));
		infot.fd = infot.fd;
		memcpy(&infot.sin_addr,&client_info.sin_addr,sizeof(struct in_addr));
		
		//创建线程接收数据
		pthread_t tid;
		pthread_create(&tid,NULL,client_routine,(void*)&infot);
	}

	close(socket_tcp);
	return 0;
}














